﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace xmldict {
	/// <summary>
	/// Description of Dict.
	/// </summary>
	public class Dict:IDict
	{
		private IMinimalDict source;
		
		private HashSet<string> allowedKeys;
		private HashSet<string> readonlyKeys;
		
		public Dict(IMinimalDict source) {
			this.allowedKeys = new HashSet<string>();
			this.readonlyKeys = new HashSet<string>();
			this.source = source;
		}

#region	IMinimalDict
		public object this[string key] {
			get {
				if (!keyValid(key) || ! keyAllowed(key)){
					return null;
				}
				return source[key];
			}
			set {
				if (!keyValid(key)|| ! keyAllowed(key) || keyReadonly(key)){
					return ;
				}
				source[key] = value;
			}
		}
		
		public int Count {
			get {
				return Keys.Count;
			}
		}
		
		public System.Collections.Generic.ICollection<string> Keys {
			get {
				HashSet<string> k = new HashSet<string>(source.Keys);
				if (allowedKeys.Count >0){
					k.IntersectWith(allowedKeys);
				}
				return new ReadOnlyCollection<string>(new List<string>(k));
			}
		}
		
		
		public bool ContainsKey(string key)
		{
			return keyValid(key) && keyAllowed(key) && source.ContainsKey(key);
		}
		
		public void Add(string key, object value)
		{
			this[key] = value;
		}
		
		public void Remove(string key)
		{
			if (keyValid(key) && keyAllowed(key) && ! keyReadonly(key)){
				source.Remove(key);
			}			
		}
		
		public bool TryGetValue(string key, out object value)
		{
			if (this.ContainsKey(key)){
				value = this[key];
				return true;
			} else {
				value = null;
				return false;
			}
		}
		
		public void Clear()
		{
			foreach (string key in Keys){
				Remove(key);
			}
		}
		
		public void Update(IMinimalDict other)
		{
			if (other == null) {
				return;
			}
			foreach (string key in other.Keys){
				this[key] = other[key];
			}
		}
		
		public void Update(System.Collections.Generic.Dictionary<string, object> other)
		{
			if (other == null) {
				return;
			}
			foreach (string key in other.Keys){
				this[key] = other[key];
			}
		}
		
		public void CreateValue(string key)
		{
			if (!this.ContainsKey(key)){
				this[key] = NewValue(key);
			}
		}
		
		public object NewValue(string key)
		{
			return newValueInternal(key);
		}
		
		protected virtual object newValueInternal(string key){
			return null;
		}
#endregion		
#region IDict 		
		public void addAllowed(params string[] keys)
		{
			foreach (string key in keys) {
				if (keyValid(key)){
					allowedKeys.Add(key);
				}
			}
		}
		
		public void delAllowed(params string[] keys)
		{
			allowedKeys.ExceptWith(keys);
			readonlyKeys.ExceptWith(keys);
		}
		
		public void addReadonly(params string[] keys)
		{
			foreach (string key in keys) {
				if (keyValid(key) && keyAllowed(key)){
					readonlyKeys.Add(key);
				}
			}
		}
		
		public void delReadonly(params string[] keys)
		{
			readonlyKeys.ExceptWith(keys);
		}
		
		public bool keyAllowed(string key)
		{
			return allowedKeys.Count == 0 || allowedKeys.Contains(key);
		}
		
		public bool keyReadonly(string key)
		{
			return readonlyKeys.Count == 0 || readonlyKeys.Contains(key);
		}
		
		public bool keyValid(string key)
		{
			return key != null;
		}
#endregion
	}
}
